package com.avcompris.util.junit;

import static org.apache.commons.lang3.CharEncoding.UTF_8;
import static com.avcompris.util.ExceptionUtils.nonNullArgument;
import static org.apache.commons.io.FileUtils.openInputStream;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import com.avcompris.util.AbstractUtils;

/**
 * utilities to test against XSLT stylesheets.
 * 
 * @author David Andriana Copyright Avantage Compris SARL 2009-2010 ©
 */
public abstract class XsltTestUtils extends AbstractUtils {

	/**
	 * call a given template within a XSLT sheet and return its result.
	 * 
	 * @param xsltFile
	 *            the XSLT file.
	 * @param templateName the XSL template to call.
	 * @param reader the reader to the XML content.
	 * @param params
	 *            the params, as key/value paris, to pass to the XSLT sheets.
	 * @return the result.
	 */
	public static String callXslTemplate(
			final File xsltFile,
			final String templateName,
			final Reader reader,
			final String... params) throws TransformerFactoryConfigurationError, TransformerConfigurationException, TransformerException, IOException {

		nonNullArgument(xsltFile, "xsltFile");
		nonNullArgument(templateName, "templateName");
		nonNullArgument(reader, "reader");
		nonNullArgument(params, "params");

		final StringBuilder xsl = new StringBuilder();
		xsl.append("<xsl:stylesheet"
				+ " xmlns:xsl='http://www.w3.org/1999/XSL/Transform'"
				+ " version='1.0'>");
		xsl.append("<xsl:import href='" + xsltFile.getPath() + "'/>");
		xsl.append("<xsl:output method='text'/>");
		xsl.append("<xsl:template match='/'>");
		xsl.append("<xsl:call-template name='" + templateName + "'>");

		for (int i = 0; i < params.length; i += 2) {

			xsl.append("<xsl:with-param name='" + params[i] + "'>"
					+ params[i + 1] + "</xsl:with-param>");
		}

		xsl.append("</xsl:call-template>");
		xsl.append("</xsl:template>");
		xsl.append("</xsl:stylesheet>");

		final TransformerFactory txFactory = TransformerFactory.newInstance();

		final Transformer tx = txFactory.newTransformer(new StreamSource(
				new StringReader(xsl.toString())));

		final Writer output = new StringWriter();

		tx.transform(new StreamSource(reader), new StreamResult(output));

		return output.toString();
	}

	/**
	 * call a given template within a XSLT sheet and return its result.
	 * 
	 * @param xsltFile
	 *            the XSLT file.
	 * @param templateName the XSL template to call.
	 * @param params
	 *            the params, as key/value paris, to pass to the XSLT sheets.
	 * @return the result.
	 */
	public static String callXslTemplate(
			final File xsltFile,
			final String templateName,
			final String... params) throws TransformerFactoryConfigurationError, TransformerConfigurationException, TransformerException, IOException {

		return callXslTemplate(xsltFile, templateName, new StringReader(
				"<xml/>"), params);
	}

	/**
	 * call a given template within a XSLT sheet and return its result.
	 * 
	 * @param xsltFile
	 *            the XSLT file.
	 * @param templateName the XSL template to call.
	 * @param xmlFile the XML content file.
	 * @param params
	 *            the params, as key/value paris, to pass to the XSLT sheets.
	 * @return the result.
	 */
	public static String callXslTemplate(
			final File xsltFile,
			final String templateName,
			final File xmlFile,
			final String... params) throws TransformerFactoryConfigurationError, TransformerConfigurationException, TransformerException, IOException {

		nonNullArgument(xsltFile, "xsltFile");
		nonNullArgument(templateName, "templateName");
		nonNullArgument(xmlFile, "xmlFile");
		nonNullArgument(params, "params");

		final InputStream is = openInputStream(xmlFile);
		try {

			return callXslTemplate(xsltFile, templateName,
					new InputStreamReader(is, UTF_8), params);

		} finally {
			is.close();
		}
	}
}
